package com.xiaodou.mjf.infrastructure.generate;

import java.io.File;
import java.sql.*;
import java.util.*;

import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.xiaodou.mjf.infrastructure.common.utils.FreeMarkers;
import com.xiaodou.mjf.infrastructure.generate.domain.Entity;
import com.xiaodou.mjf.infrastructure.generate.domain.Field;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.google.common.collect.Lists;
import com.xiaodou.mjf.infrastructure.common.utils.StringUtils;

/**
 * Created by Rocky on 15/1/3.
 */
public class EntityGenerator extends ParentGenerator {

    List<String> ignoreFields = Lists.newArrayList("ID","STATUS","CREATOR","MODIFIER","CREATE_TIME","MODIFY_TIME","VERSION");

    String moduleName = null;
    File entityDest,appDest;
    String entityPackage = null,applicationPackage = null;
    Configuration cfg = new Configuration();
    Template template;

    @Override
    public void generate(XSSFWorkbook workbook, File dest) throws Exception {
        init();

        entityPackage = props.getProperty("generator.entity");
        applicationPackage = props.getProperty("generator.application");

        Connection connection = getConnection();

        PreparedStatement pstmt = null;

        if (connection == null) {
            logger.error("DB connect fail!");
            return;
        }

        Iterator<Row> rowIterator = workbook.getSheetAt(0).rowIterator();
        while (rowIterator.hasNext()) {
            Row row = rowIterator.next();

            //filter the header row
            if (row.getRowNum() == 0) {
                continue;
            }

            //if enable == 0 then skip the recoed
            int enable = (int) row.getCell(0).getNumericCellValue();
            if (enable == 0) {
                continue;
            }

            String tableNamePrefix = row.getCell(1).getStringCellValue();
            if (StringUtils.isBlank(tableNamePrefix)) {
                logger.error("data error at row#{},tableName can not be null", row.getRowNum());
            }

            moduleName = row.getCell(2).getStringCellValue();
            if (StringUtils.isBlank(moduleName)) {
                logger.error("data error at row#{},moduleName can not be null", row.getRowNum());
            }

            while (!new File(dest.getPath() + File.separator + entityPackage).exists()) {
                dest = dest.getParentFile();
            }

            String template = dest.getPath() + File.separator + templatePath;
            entityDest = new File(dest.getPath() + File.separator + entityPackage);
            appDest = new File(dest.getPath() + File.separator + applicationPackage);

            cfg.setDirectoryForTemplateLoading(new File(template));

            //pstmt = connection.prepareStatement(String.format("select * from USER_TABLES where TABLE_NAME like '%s%%'", tableNamePrefix));
            //H2数据库不解析show tables like '%cif%',暂以代码过滤
            pstmt = connection.prepareStatement(String.format("show tables like '%s'",tableNamePrefix));
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                String tableName = rs.getString(1);
                //if(StringUtils.isNotBlank(exclude) && !tableName.startsWith(exclude))
                //if(tableName.startsWith(tablePrefix) || tableName.startsWith(StringUtils.lowerCase(tablePrefix)) || tableName.startsWith(StringUtils.upperCase(tablePrefix))){
                createEntity(tableName, pstmt, connection);
                //}
            }

        }
    }


    private void createEntity(String tableName,PreparedStatement pstmt,Connection connection) throws Exception{

        DatabaseMetaData dbmd = connection.getMetaData();

        Entity entity = new Entity();
        entity.setClassName(getClassName(tableName));
        entity.setTableName(tableName);
        entity.setSerialNum(new Random().nextLong());

        ResultSet rs = dbmd.getColumns(connection.getCatalog(), "%", tableName, "%");
        while(rs.next()){

            String colmnName = rs.getString("COLUMN_NAME");

            if(ignoreFields.contains(colmnName.toUpperCase())){
                continue;
            }

            String columnType = rs.getString("TYPE_NAME");
            String comments = rs.getString("REMARKS");
            int nullable = rs.getInt("NULLABLE");

            Field field = new Field();
            field.setFieldName(getFieldName(colmnName));
            field.setFieldType(getFieldType(columnType,entity));
            field.setColumnName(colmnName);
            field.setAllowNull(nullable == 1);
            field.setUpperFieldName(StringUtils.upperFirst(field.getFieldName()));
            field.setComments(comments);

            entity.getFields().add(field);
        }

        Map<String, Object> map = Maps.newHashMap();
        map.put("entity", entity);
        map.put("currentDate", DateFormatUtils.format(Calendar.getInstance().getTime(), "yyyy-MM-dd HH:mm:ss"));
        map.put("moduleName", moduleName);

        //EntityBase.ftl
        template = cfg.getTemplate("EntityBase.ftl");
        String content = FreeMarkers.renderTemplate(template, map);
        String filePath = StringUtils.replace(entityDest.getPath()+ "/src/main/java/" + StringUtils.replace(entityPackage + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/base/"+entity.getClassName()+"Base.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate EntityBase done : {}",filePath);
        System.out.println(filePath);

        //Entity.ftl
        template = cfg.getTemplate("Entity.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(entityDest.getPath()+ "/src/main/java/" + StringUtils.replace(entityPackage + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/"+entity.getClassName()+".java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate Entity done : {}",filePath);
        System.out.println(filePath);

        //Dao.ftl
        template = cfg.getTemplate("Dao.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(appDest.getPath()+ "/src/main/java/" + StringUtils.replace(applicationPackage + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/repository/"+entity.getClassName()+"Repository.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate Dao done : {}",filePath);
        System.out.println(filePath);

        //Service.ftl
        template = cfg.getTemplate("Service.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(appDest.getPath()+ "/src/main/java/" + StringUtils.replace(applicationPackage + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/service/"+entity.getClassName()+"Service.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate Service done : {}",filePath);
        System.out.println(filePath);

        //ServiceImpl.ftl
        template = cfg.getTemplate("ServiceImpl.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(appDest.getPath()+ "/src/main/java/" + StringUtils.replace(applicationPackage + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/service/impl/"+entity.getClassName()+"ServiceImpl.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate ServiceImpl done : {}",filePath);
        System.out.println(filePath);

        //Manager.ftl
        /*
        template = cfg.getTemplate("Manager.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(appDest.getPath()+ "/src/main/java/" + StringUtils.replace(props.getProperty("app.package") + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/manager/"+entity.getClassName()+"Manager.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate Manager done : {}",filePath);
        System.out.println(filePath);
        */

        //ManagerImpl.ftl
        /*
        template = cfg.getTemplate("ManagerImpl.ftl");
        content = FreeMarkers.renderTemplate(template, map);
        filePath = StringUtils.replace(appDest.getPath()+ "/src/main/java/" + StringUtils.replace(props.getProperty("app.package") + FilenameUtils.EXTENSION_SEPARATOR_STR + moduleName, FilenameUtils.EXTENSION_SEPARATOR_STR, File.separator) + "/manager/impl/"+entity.getClassName()+"ManagerImpl.java", "/", File.separator);
        write(content, filePath);
        logger.debug("Generate ManagerImpl done : {}",filePath);
        System.out.println(filePath);
        */
    }

    private String getFieldType(String sqlType,Entity entity){
        sqlType = StringUtils.lowerCase(sqlType);
        if(props.containsKey(sqlType)){
            String type = props.getProperty(sqlType);
            if(type.equals("Date")){
                entity.getImports().add("java.util.Date");
            }
            return type;
        }else{
            logger.error("没有找到类型{}映射，请在mappings.properties里添加",sqlType);
            System.err.println("没有找到类型"+sqlType+"映射，请在mappings.properties里添加");
            return StringUtils.EMPTY;
        }
    }

    private String getClassName(String tableName){

        String className = StringUtils.EMPTY;
        for(String str : Splitter.on("_").split(tableName)){
            className += StringUtils.upperFirst(StringUtils.lowerCase(str));
        }
        return className;
    }

    private String getFieldName(String columnName){
        String fieldName = StringUtils.EMPTY;
        for(String str : Splitter.on("_").split(columnName)){
            fieldName += StringUtils.upperFirst(StringUtils.lowerCase(str));
        }
        return StringUtils.lowerFirst(fieldName);
    }




    private Connection getConnection(){
        try{
            Class.forName(props.getProperty("jdbc.driver"));
            Connection connection = DriverManager.getConnection(props.getProperty("jdbc.url"),props.getProperty("jdbc.username"),props.getProperty("jdbc.password"));
            return connection;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
